<?php
/**
 * php设计模式-观测者模式
 * 概念:其实观察者模式这是一种较为容易去理解的一种模式吧，它是一种事件系统，意味
 *      着这一模式允许某个类观察另一个类的状态，当被观察的类状态发生改变的时候，
 *      观察类可以收到通知并且做出相应的动作;观察者模式为您提供了避免组件之间
 *      紧密耦合的另一种方法
 * 关键点:
 *        1.被观察者->追加观察者;->移除观察者;->满足条件时通知观察者;->观察条件
 *        2.观察者 ->接受观察方法
 * 缺点:
 * 观察者模式在PHP中的应用场合:在web开发中观察者应用的方面很多
 *      典型的:用户注册(验证邮件，用户信息激活)，购物网站下单时邮件/短信通知等
 * php内部的支持
 *      SplSubject 接口，它代表着被观察的对象，
 *      其结构：
 *      interface SplSubject
 *      {
 *          public function attach(SplObserver $observer);
 *          public function detach(SplObserver $observer);
 *          public function notify();
 *      }
 *      SplObserver 接口，它代表着充当观察者的对象，
 *      其结构：
 *      interface SplObserver
 *      {
 *          public function update(SplSubject $subject);
 *      }
 */

/**
 * 用户登陆
 */
class User implements SplSubject
{
    /**
     * 被注册的观察者保存容器
     * @var SplObjectStorage
     */
    public $observers;
    
    /**
     * User constructor.
     */
    public function __construct()
    {
        $this->observers = new SplObjectStorage();
    }
    
    
    /**
     * 追加观察者
     * @param SplObserver $observer 观察者
     */
    public function attach(SplObserver $observer)
    {
        //$this->observers[] = $observer;
        $this->observers->attach($observer);
    }
    
    /**
     * 去除观察者
     * @param SplObserver $observer 观察者
     */
    public function detach(SplObserver $observer)
    {
        $this->observers->detach($observer);
        //if ( $idx = array_search ($observer , $this->observers , true) ) {
        //	unset($this->observers[ $idx ]);
        //}
    }
    
    /**
     * 满足条件时通知观察者
     */
    public function notify()
    {
        if (null !== $this->observers) {
            /** @var SplObserver $observer */
            foreach ($this->observers as $observer) {
                //通知观察者
                $observer->update($this);
            }
        }
    }
    
    /**
     * 添加用户
     * @return bool
     */
    public function addUser() : bool
    {
        //执行sql
        //数据库插入成功
        $res = true;
        //调用通知观察者
        $this->notify();
        
        return $res;
    }
    
    /**
     * 用户信息编辑
     * @return bool
     */
    public function editUser() : bool
    {
        //执行sql
        //数据库更新成功
        $res = true;
        //调用通知观察者
        $this->notify();
        
        return $res;
    }
}

/**
 * 观察者1-发送邮件
 */
class SendMail implements SplObserver
{
    /**
     * 相应被观察者的变更信息
     * @param SplSubject $subject
     */
    public function update(SplSubject $subject)
    {
        $title   = '';
        $content = '';
        $email   = '';
        $this->sendEmail($email, $title, $content);
    }
    
    /**
     * 发送邮件
     * @param $email
     * @param $title
     * @param $content
     */
    public function sendEmail($email, $title, $content)
    {
        //调用邮件接口，发送邮件
        echo $email, $title, $content;
        echo 'send email ok!';
    }
}

/**
 * 观察者2-发送短信
 */
class sendMms implements SplObserver
{
    /**
     * 相应被观察者的变更信息
     * @param SplSubject $subject
     */
    public function update(SplSubject $subject)
    {
        $title   = '';
        $content = '';
        $mms     = '';
        $this->sendMsg($mms, $title, $content);
    }
    
    /**
     * 发送短信
     * @param $mms
     * @param $title
     * @param $content
     */
    public function sendMsg($mms, $title, $content)
    {
        echo $mms, $title, $content;
        //调用短信接口，发送短信
        echo 'send mms ok!';
    }
}


// 被观察者
$user = new User();

// 观察者
$mms   = new sendMms();
$email = new SendMail();

// 增加观察者
$user->attach($mms);
$user->attach($email);

// 创建用户，触发通知（notify）（通知观察者进行相应操作）
$user->addUser();